//
//  DOMParser.m
//  PriceOnline
//
//  Created by Phuong Dinh Le on 1/17/13.
//  Copyright (c) 2013 WorkLikeDance. All rights reserved.
//

#import "DOMParser.h"

NSDictionary* GetDictionaryForNode(xmlNodePtr currentNode, NSMutableDictionary *parentResult)
{
	NSMutableDictionary *resultForNode = [NSMutableDictionary dictionary];
	
	if (currentNode->name)
	{
		NSString *currentNodeContent =
        [NSString stringWithCString:(const char *)currentNode->name encoding:NSUTF8StringEncoding];
		[resultForNode setObject:currentNodeContent forKey:KEY_NODE_NAME];
	}
	
	if (currentNode->content && currentNode->type != XML_DOCUMENT_TYPE_NODE)
	{
        /* CDATA content */
        if (currentNode->type == XML_CDATA_SECTION_NODE && [resultForNode objectForKey:KEY_NODE_NAME] == nil)
        {
            [resultForNode setObject:KEY_NODE_CONTENT_NAME forKey:KEY_NODE_NAME];
        }
        
		NSString *currentNodeContent =
        [NSString stringWithCString:(const char *)currentNode->content encoding:NSUTF8StringEncoding];
		
		if ([[resultForNode objectForKey:KEY_NODE_NAME] isEqual:KEY_NODE_CONTENT_NAME] && parentResult)
		{
			currentNodeContent = [currentNodeContent
                                  stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
			
			NSString *existingContent = [parentResult objectForKey:KEY_NODE_CONTENT];
			NSString *newContent;
			if (existingContent)
			{
                if (currentNodeContent)
                {
                    newContent = [existingContent stringByAppendingString:currentNodeContent];
                }
                else
                {
                    newContent = existingContent;
                }
			}
			else
			{
				newContent = currentNodeContent;
			}
            
			[parentResult setObject:newContent forKey:KEY_NODE_CONTENT];
			return nil;
		}
		
		[resultForNode setObject:currentNodeContent forKey:KEY_NODE_CONTENT];
	}
	
	xmlAttr *attribute = currentNode->properties;
	if (attribute)
	{
		//NSMutableArray *attributeArray = [NSMutableArray array];
        NSMutableDictionary *attributeDictionaries = [NSMutableDictionary dictionary];
        
		while (attribute)
		{
			NSMutableDictionary *attributeDictionary = [NSMutableDictionary dictionary];
            
			NSString *attributeName =
            [NSString stringWithCString:(const char *)attribute->name encoding:NSUTF8StringEncoding];
			if (attributeName)
			{
				[attributeDictionary setObject:attributeName forKey:KEY_ATTRIBUTE_NAME];
			}
			
			if (attribute->children)
			{
				NSDictionary *childDictionary = GetDictionaryForNode(attribute->children, attributeDictionary);
				if (childDictionary)
				{
					[attributeDictionary setObject:childDictionary forKey:KEY_ATTRIBUTE_CONTENT];
				}
			}
			
			if ([attributeDictionary count] > 0)
			{
                NSString* key = [attributeDictionary objectForKey:KEY_ATTRIBUTE_NAME];
                NSString* value = [attributeDictionary objectForKey:KEY_NODE_CONTENT];
                
                if (key && value)
                {
                    [attributeDictionaries setObject:value forKey:key];
                }
                
				//[attributeArray addObject:attributeDictionary];
			}
            
			attribute = attribute->next;
		}
		
		//if ([attributeArray count] > 0)
        if ([attributeDictionaries count] > 0)
		{
			//[resultForNode setObject:attributeArray forKey:KEY_NODE_ATTRIBUTE];
            [resultForNode setObject:attributeDictionaries forKey:KEY_NODE_ATTRIBUTE];
		}
	}
    
	xmlNodePtr childNode = currentNode->children;
	if (childNode)
	{
		NSMutableArray *childContentArray = [NSMutableArray array];
		while (childNode)
		{
			NSDictionary *childDictionary = GetDictionaryForNode(childNode, resultForNode);
			if (childDictionary)
			{
				[childContentArray addObject:childDictionary];
			}
			childNode = childNode->next;
		}
		if ([childContentArray count] > 0)
		{
			[resultForNode setObject:childContentArray forKey:KEY_NODE_CHILD];
		}
	}
	
	return resultForNode;
}

NSArray* ParseDocument(xmlDocPtr doc, NSString *xpath, NSString* namespace_S, NSString* namespace_NS2)
{
    xmlXPathContextPtr xpathCtx;
    xmlXPathObjectPtr xpathObj;
    
    /* Create xpath evaluation context */
    xpathCtx = xmlXPathNewContext(doc);
    
    if(xpathCtx == NULL)
	{
		return nil;
    }
    
    /* Register namespace for xpath */
    if (namespace_S != nil)
    {
        xmlChar *s = (xmlChar *)[namespace_S cStringUsingEncoding:NSUTF8StringEncoding];
        xmlXPathRegisterNs(xpathCtx,  BAD_CAST "S", BAD_CAST s);
    }
    
    if (namespace_NS2 != nil)
    {
        xmlChar *ns2 = (xmlChar *)[namespace_NS2 cStringUsingEncoding:NSUTF8StringEncoding];
        xmlXPathRegisterNs(xpathCtx,  BAD_CAST "ns2", BAD_CAST ns2);
    }
    
    /* Evaluate xpath expression */
    xpathObj = xmlXPathEvalExpression((xmlChar *)[xpath cStringUsingEncoding:NSUTF8StringEncoding], xpathCtx);
    if(xpathObj == NULL)
    {
		return nil;
    }
	
	xmlNodeSetPtr nodes = xpathObj->nodesetval;
	if (!nodes)
	{
		return nil;
	}
	
	NSMutableArray *resultNodes = [NSMutableArray array];
	for (NSInteger i = 0; i < nodes->nodeNr; i++)
	{
		NSDictionary *nodeDictionary = GetDictionaryForNode(nodes->nodeTab[i], nil);
		if (nodeDictionary)
		{
			[resultNodes addObject:nodeDictionary];
		}
	}
    
    /* Cleanup */
    xmlXPathFreeObject(xpathObj);
    xmlXPathFreeContext(xpathCtx);
    
    return resultNodes;
}

@implementation DOMParser
+ (NSArray*) queryHTMLElementsByXPath:(NSString *)document xpath:(NSString *)xpath namespaceS:(NSString*)namespaceS namespaceNS2:(NSString*)namespaceNS2
{
    NSData* dataDocument = [document dataUsingEncoding:NSUTF8StringEncoding];
    
    if (dataDocument == NULL)
    {
        return nil;
    }
    
    xmlDocPtr doc;
    
    /* Load XML document */
	doc = htmlReadMemory([dataDocument bytes], [dataDocument length], "", NULL, HTML_PARSE_NOWARNING | HTML_PARSE_NOERROR);
	
    if (doc == NULL)
	{
		return nil;
    }

	NSArray *result = ParseDocument(doc, xpath, namespaceS, namespaceNS2);
    xmlFreeDoc(doc);
	
	return result;
}

+ (NSArray*) queryXMLNodesByXPath:(NSString *)document xpath:(NSString *)xpath namespaceS:(NSString*)namespaceS namespaceNS2:(NSString*)namespaceNS2
{
    NSData* dataDocument = [document dataUsingEncoding:NSUTF8StringEncoding];

    if (dataDocument == NULL)
    {
        return nil;
    }
    
    xmlDocPtr doc;
	
    /* Load XML document */
	doc = xmlReadMemory([dataDocument bytes], [dataDocument length], "", NULL, XML_PARSE_RECOVER);
	
    if (doc == NULL)
	{
		return nil;
    }
	
	NSArray *result = ParseDocument(doc, xpath, namespaceS, namespaceNS2);
    xmlFreeDoc(doc); 
	
	return result;
}
@end
